package com.ljxt.dt.util.xhq;

import com.alibaba.druid.util.JdbcUtils;
import com.ljxt.dt.vo.purchasingmanagement.CgddHandlerInfo;
import com.sun.javafx.fxml.builder.JavaFXFontBuilder;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.FilenameFilter;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.sql.ResultSet;
import java.util.*;

/**
 * @author 徐浩钦
 * @version 1.0.0
 * @date 2020/5/2 9:25
 * @copyright 老九学堂T11
 */
public class ClassUtil implements Serializable {

    /**
     * 传入一个包的路径，根据内部解析找到的 .class 文件进行挨个实例化
     * @param pagePath 包路径地址
     * @return 返回的 class 类对象集合
     */
    public static List<Class> pageAnalysis(String pagePath){
        List<Class> returned = new ArrayList<>();

        // 思路分析
        String pagePathFormat = pagePath.replace(".","/");

        // 根据调用者的线程获取上下文路径
        URL url = Thread.currentThread().getContextClassLoader().getResource(pagePathFormat);

        // 根据 url 路径创建一个文件对象
        File file = new File(url.getFile());

        // 根据包文件对象筛选其中后缀为 .class 的文件数组
        File[] files = file.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(".class");
            }
        });

        // 遍历筛选出来的文件数组
        for (File tempFile:
             files) {
            // 拼接为可以实例化为 class 对象的字符串，包名 + 文件名 -> 去除后缀 .class
            String tempStr = pagePath + "." +tempFile.getName().replace(".class","");

            // 实例化为 class 对象
            Class<?> clazz = null;
            try {
                clazz = Class.forName(tempStr);
            } catch (Exception e) {
                e.printStackTrace();
            }

            // 将结果添加在最终返回的结果中
            returned.add(clazz);
        }

        return returned;
    }


    /**
     * 将对象的成员属性和值转换为 map 的形式
     * @param object 需要转换的 obj
     * @return 返回一个 map 形式的属性名和值的对应
     * @throws IntrospectionException
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     */
    public static Map<String, Object> objToMap(Object object) {
        // 创建一个最终返回的 map 对象
        Map<String, Object> result = new HashMap<>();

        // 获取对象的类对象
        Class<?> clazz = object.getClass();

        // 根据 类对象 获取 BeanInfo
        BeanInfo beanInfo = null;
        try {
            beanInfo = Introspector.getBeanInfo(clazz);
        } catch (IntrospectionException e) {
            e.printStackTrace();
        }

        // 根据获取到的BeanInfo 对象获取到属性描述符
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();

        // 循环属性描述符
        for (PropertyDescriptor propertyDescriptor:
                propertyDescriptors) {

            // 获取 读取的方法 和 成员属性的类型
            Method method = propertyDescriptor.getReadMethod();
            Class type = propertyDescriptor.getPropertyType();

            // 获取成员属性名 - 用于键
            String name = propertyDescriptor.getName();

            // 通过 读取方法读取 成员属性
            Object tempObj = null;
            try {
                tempObj = method.invoke(object);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }

            if(tempObj == null){
                continue;
            }


            // 判断类型并进行转换
            if(tempObj instanceof String){
                result.put(name,tempObj.toString());

            }else if(tempObj instanceof Date){
                result.put(name,tempObj);

            }else {
                    // 判断是包装数据类型
                String s = tempObj.toString();

                // 判断类型并转换
                if(type.toString().equals(Integer.class.toString())){
                    result.put(name,Integer.parseInt(s));

                }else if(type.toString().equals(Byte.class.toString())){
                    result.put(name,Byte.parseByte(s));

                }else if(type.toString().equals(Short.class.toString())){
                    result.put(name,Short.parseShort(s));

                }else if(type.toString().equals(Long.class.toString())){
                    result.put(name,Long.parseLong(s));

                }else if(type.toString().equals(Float.class.toString())){
                    result.put(name,Float.parseFloat(s));

                }else if(type.toString().equals(Double.class.toString())){
                    result.put(name,Double.parseDouble(s));
                }else if(type.toString().equals(Boolean.class.toString())){
                    result.put(name,Boolean.parseBoolean(s));

                }else if(tempObj.getClass().isPrimitive()){

                    if(type.equals(int.class)){
                        result.put(name,(int)tempObj);
                    }else if(type.equals(byte.class)){
                        result.put(name,(byte)tempObj);
                    }else if(type.equals(short.class)){
                        result.put(name,(short)tempObj);
                    }else if(type.equals(long.class)){
                        result.put(name,(long)tempObj);
                    }else if(type.equals(double.class)){
                        result.put(name,(double)tempObj);
                    }else if(type.equals(float.class)){
                        result.put(name,(float)tempObj);
                    }else if(type.equals(boolean.class)){
                        result.put(name,(boolean)tempObj);
                    }else if(type.equals(char.class)){
                        result.put(name,(char)tempObj);
                    }

                }
            }

        }

        // 返回最终结果
        return result;
    }

    /**
     * 这个是将 一个对象自动生成 inner into 的方法，值为 null 的不会添加
     * @param tableName 表明
     * @param object 对象
     * @return 返回 map 里 key 为 sql 获取 sql 语句，key 为 list 获取 参数集合
     */
    public static Map<String,Object> objInsertToSql(String tableName, Object object){
        Map<String,Object> map = new HashMap<>();
        List<Object> objects = new ArrayList<>();

        StringBuilder sql = (new StringBuilder()).append("insert into ").append(tableName).append("(");
        // 获取对象的类对象
        Class<?> clazz = object.getClass();

        // 根据 类对象 获取 BeanInfo
        BeanInfo beanInfo = null;
        try {
            beanInfo = Introspector.getBeanInfo(clazz);
        } catch (IntrospectionException e) {
            e.printStackTrace();
        }

        // 根据获取到的BeanInfo 对象获取到属性描述符
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();

        StringBuilder val = (new StringBuilder()).append(" ) values ( ");
        // 循环属性描述符
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
           String name = StringUtil.tuoFengToXiaHua(propertyDescriptor.getName());
           if(name.equals("class")){
               continue;
           }


            Method readMethod = propertyDescriptor.getReadMethod();


            Object obj = null;
            try {
                obj = readMethod.invoke(object);

                // 如果没有设置的则不添加在语句中
                if(obj == null){
                    continue;
                }

                sql.append(name + " , ");
                objects.add(obj);

            } catch (Exception e) {
                e.printStackTrace();
            }

            val.append("? , ");
        }
        String res = sql.substring(0,sql.lastIndexOf(",")-1).toString() + val.substring(0,val.lastIndexOf(",")-1).toString();
        res += ")";


        map.put("sql",res);
        map.put("list",objects);
        return map;
    }

    /**
     * 聚合 stringUtil 里的 mapToSql 方法和 objToMap 方法的快捷方式;
     * @param o 需要转换的对象
     * @return 返回值的 map 中 key="sql" 可以取出 sql 语句，key="array" 可以取出参数数组
     */
    public static Map<String, Object> objToString(Object o){
      return StringUtil.mapToSql(objToMap(o));
    };

    /**
     * 可自适应的 RowMapper
     * @param resultSet 循环迭代的 数据集对象
     * @param clazz 需要取的对象属性
     * @param <T> 类型
     * @return 返回值
     */
    public static <T> T rouMapper(ResultSet resultSet, Class<T> clazz){
        T returned = null;
        try {
            returned = clazz.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        // 思路分析
        // 1.
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor pro:
                 propertyDescriptors) {

                String name = pro.getName();
                Class<?> type = pro.getPropertyType();
                // 将驼峰转下划线方式
                String strName = StringUtil.tuoFengToXiaHua(name);

                // 判断类型并转换
                if(type.toString().equals(Integer.class.toString()) || type.toString().equals(int.class.toString())){
                    // integer // int
                    pro.getWriteMethod().invoke(returned,resultSet.getInt(strName));

                }else if(type.toString().equals(Short.class.toString())){
                    // short
                    pro.getWriteMethod().invoke(returned,resultSet.getShort(strName));

                }else if(type.toString().equals(Long.class.toString())){
                    // long
                    pro.getWriteMethod().invoke(returned,resultSet.getLong(strName));

                }else if(type.toString().equals(Float.class.toString())){
                    // float
                    pro.getWriteMethod().invoke(returned,resultSet.getFloat(strName));

                }else if(type.toString().equals(Double.class.toString())){
                    // double
                    pro.getWriteMethod().invoke(returned,resultSet.getDouble(strName));

                }else if(type.toString().equals(Byte.class.toString())){
                    // type 类型不做设置

                }else if (type.toString().equals(String.class.toString())){
                    // string 字符串处理
                    pro.getWriteMethod().invoke(returned,resultSet.getString(strName));

                }else if(type == Date.class){
                    // date 时间处理
                    pro.getWriteMethod().invoke(returned,resultSet.getDate(strName));
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return returned;
    };

    /**
     * 将一个存储 map 的 list 转换为存储指定对象的 list 集合
     * @param maps
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> List<T> listMapToListObj(List<Map<String,Object>> maps,Class<T> clazz){

        List<T> result = new ArrayList<>();

        // 将 list map 的结构集合反射取出对应的值给对象
        BeanInfo beanInfo = null;
        try {
            beanInfo = Introspector.getBeanInfo(clazz);
        } catch (IntrospectionException e) {
            e.printStackTrace();
        }

        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();

        for (Map<String, Object> map:
                maps) {

            T object = null;

            try {
                object = (T) clazz.newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }

            for (PropertyDescriptor pro:
                    propertyDescriptors) {
                // 获取属性类型
                Class type = pro.getPropertyType();
                // 从 map 中根据属性名获取对应的值
                Object tempObj = map.get(pro.getName());
                // 如果值不存在则跳过本次属性映射的循环
                if(tempObj == null){
                    continue;
                }

                try {
                    // 因为用于数据库的 domain 层之间的装换
                    // 判断类型并进行转换
                    if(tempObj instanceof String){
                        pro.getWriteMethod().invoke(object,tempObj.toString());

                    }else if(tempObj instanceof Date){
                        pro.getWriteMethod().invoke(object,(Date)tempObj);

                    }else if(tempObj.getClass().isPrimitive()){
                        String s = tempObj.toString();

                        // 判断类型并转换
                        if(type.toString().equals(Integer.class.toString())){
                            pro.getWriteMethod().invoke(object,Integer.parseInt(s));

                        }else if(type.toString().equals(Byte.class.toString())){
                            pro.getWriteMethod().invoke(object,Byte.parseByte(s));

                        }else if(type.toString().equals(Short.class.toString())){
                            pro.getWriteMethod().invoke(object,Short.parseShort(s));

                        }else if(type.toString().equals(Long.class.toString())){
                            pro.getWriteMethod().invoke(object,Long.parseLong(s));

                        }else if(type.toString().equals(Float.class.toString())){
                            pro.getWriteMethod().invoke(object,Float.parseFloat(s));

                        }else if(type.toString().equals(Double.class.toString())){
                            pro.getWriteMethod().invoke(object,Double.parseDouble(s));

                        }else if(type.toString().equals(Boolean.class.toString())){
                            pro.getWriteMethod().invoke(object,Boolean.parseBoolean(s));

                        }
                    }else{
                        pro.getWriteMethod().invoke(object,tempObj);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }

            result.add(object);

        }


        return result;
    }

    /**
     * map 变成 对象的方法
     * @param maps
     * @param t
     * @param <T>
     */
    public static <T> void mapToObj(Map<String,Object> maps,T t) {
        // 根据传进来的对象获取他的 class 制作一份反射对象
        Class tClass = t.getClass();

        // 获取属性描述符的数组对象
        PropertyDescriptor[] propertyDescriptors = new PropertyDescriptor[0];
        try {
            propertyDescriptors = Introspector.getBeanInfo(tClass).getPropertyDescriptors();
        } catch (IntrospectionException e) {
            e.printStackTrace();
        }

        // 循环遍历描述符进行对应的
        for (PropertyDescriptor pro : propertyDescriptors) {
            // 获取属性类型
            Class parType = pro.getPropertyType();
            Method method =  pro.getWriteMethod();

            Object o = maps.get(pro.getName());
            if(o == null){
                continue;
            }
            String value = o.toString();

            // 进行格式转换
            parTypeTransition(parType,method,t,value);
        }
    }

    /**
     * 根据成员变量的类型进行判断，并进行对应的包装类转换
     * @param parType
     * @param method
     * @param t
     * @param value
     */
    public static void parTypeTransition(Class parType, Method method,Object t, String value){
        try {
            // 判断类型并转换
            if(parType.toString().equals(Integer.class.toString())){
                method.invoke(t,Integer.parseInt(value));

            }else if(parType.toString().equals(Byte.class.toString())){
                method.invoke(t,Byte.parseByte(value));

            }else if(parType.toString().equals(Short.class.toString())){
                method.invoke(t,Short.parseShort(value));

            }else if(parType.toString().equals(Long.class.toString())){
                method.invoke(t,Long.parseLong(value));

            }else if(parType.toString().equals(Float.class.toString())){
                method.invoke(t,Float.parseFloat(value));

            }else if(parType.toString().equals(Double.class.toString())){
                method.invoke(t,Double.parseDouble(value));

            }else if(parType.toString().equals(Boolean.class.toString())){
                method.invoke(t,Boolean.parseBoolean(value));

            }else{
                method.invoke(t,value);

            }

        }catch (Exception e){
            throw new RuntimeException("类型转换错误" + e.getMessage());
        }
    }

    public static  Map<String,Object> updataObj(String tableName,Object resource ,Object target){
        Map<String,Object> result = new HashMap<>();

        StringBuilder sql = new StringBuilder();
        sql.append("update ").append(tableName).append(" set ");

        List<Object> pars = new ArrayList<>();
        // 获取对象的类对象
        Class<?> clazz = resource.getClass();
        // 根据 类对象 获取 BeanInfo
        BeanInfo beanInfo = null;
        try {
            beanInfo = Introspector.getBeanInfo(clazz);
        } catch (IntrospectionException e) {
            e.printStackTrace();
        }
        // 根据获取到的BeanInfo 对象获取到属性描述符
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        // 循环属性描述符
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            Method readMethod = propertyDescriptor.getReadMethod();

            if (propertyDescriptor.getName().equals("class")){
                continue;
            }

            Object tempObj = null;
            try {
                tempObj = readMethod.invoke(resource);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }

            if(tempObj == null){
                continue;
            }

            String name = StringUtil.tuoFengToXiaHua(propertyDescriptor.getName());
            sql.append(name + " = ? , ");
            pars.add(tempObj);
        }

        String res = sql.substring(0,sql.lastIndexOf(",")-1);
        res += " where 1=1 ";


        Map<String, Object> map = objToString(target);
        String sql1 = map.get("sql").toString();
        Object[] os = (Object[]) map.get("array");
        res += sql1;

        for (Object o : os) {
            pars.add(o);
        }
        result.put("sql",res);
        result.put("list",pars);

        System.out.println(res);
        System.out.println(pars);
        return result;

    }

    /**
     * 根据指定的模板map 将一个对象的集合转换为 map 的形式
     * @param lists
     * @param maps
     * @param <T>
     * @return
     */
    public static <T> ArrayList<LinkedHashMap<String,Object>> listObjsToMapByMap(List<T> lists, LinkedHashMap<String,String> maps){
        ArrayList<LinkedHashMap<String,Object>> arrayList = new ArrayList<>();

        // 获取这个集合的所有对象进行遍历
        for (Object obj : lists) {
            LinkedHashMap<String,Object> linkedHashMap = new LinkedHashMap<>();
            // 遍历模板需要取出的顺序 map 数据
            for (Map.Entry<String, String> tempMap : maps.entrySet()) {
                // 获取 key
                String key = tempMap.getKey();

                PropertyDescriptor[] propertyDescriptors = null;
                try {
                    propertyDescriptors = Introspector.getBeanInfo(obj.getClass()).getPropertyDescriptors();
                } catch (IntrospectionException e) {
                    e.printStackTrace();
                }

                for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                    String tempName =  propertyDescriptor.getName();
                    if(tempName.equals(key)){
                        try {
                            linkedHashMap.put(key,propertyDescriptor.getReadMethod().invoke(obj));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            arrayList.add(linkedHashMap);
        }

        return arrayList;
    }
}